/*
 * Class representing a media of a library
 * @author M.T. Segarra
 * @version 0.0.1
 */
package mediasManagement;

import java.util.*;

import exceptions.*;

import javax.persistence.*;

import loansManagement.Loan;

import specimensManagement.Specimen;

/**
 * Entity implementation class for Entity: Media
 *
 */
@Entity
@Table(name="medias")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "media_type",
discriminatorType = DiscriminatorType.STRING
		)
@NamedQuery(name="findAllMedias", query="select m from Media m")
public abstract class Media  {

	// Fields
	/**
	 * @uml.property name="title"
	 */
	private String title;

	/**
	 * @uml.property name="issnOrIsan"
	 */
	@Column(name="isbn_isan")
	@Id
	private String isbnOrIsan;

	/**
	 * @uml.property name="editionDate"
	 */
	private Calendar editionDate;

	/**
	 * @uml.property  name="specimens"
	 * @uml.associationEnd  multiplicity="(1 -1)" ordering="true" inverse="media:specimensManagement.Specimen"
	 */
	@OneToMany(mappedBy="media" ,fetch=FetchType.EAGER)
	private Set<Specimen> specimens;

	/**
	 * Nombre d'exemplaires empruntes
	 * @uml.property name="lentSpecimens"
	 */
	private Integer lentSpecimens;

	/**
	 * @uml.property  name="nbAvailableSpecimen"
	 */
	private int nbAvailableSpecimen;

	/**
	 * @uml.property  name="nbCreatedSpecimen"
	 */
	private int nbCreatedSpecimen;

	/**
	 * @uml.property  name="loans"
	 * @uml.associationEnd  multiplicity="(0 -1)" inverse="media:loansManagement.Loan"
	 */
	@Transient
	private Set<Loan> loans;

	/**
	 * @uml.property name="type"
	 */
	private String type;

	// Property accessors
	/**
	 * Getter of the property <tt>id</tt>
	 * 
	 * @return Returns the id.
	 * @uml.property name="id"
	 */
	public String getId() {
		return isbnOrIsan;
	}

	/**
	 * Getter of the property <tt>title</tt>
	 * 
	 * @return Returns the title.
	 * @uml.property name="title"
	 */
	public String getTitle() {
		return title;
	}

	/**
	 * Setter of the property <tt>title</tt>
	 * 
	 * @param title
	 *            The title to set.
	 * @uml.property name="title"
	 */
	public void setTitle(String title) {
		this.title = title;
	}

	public int getNbAvailableSpecimen() {
		return nbAvailableSpecimen;
	}

	public void setNumberAvailableExemplary(int numberAvailableExemplary) {
		this.nbAvailableSpecimen = numberAvailableExemplary;
	}

	public Set<Loan> getLoans() {
		return loans;
	}

	public void setLoans(Set<Loan> loans) {
		this.loans = loans;
	}

	public void setId(String id) {
		this.isbnOrIsan = id;
	}

	public void setSpecimens(Set<Specimen> specimens) {
		this.specimens = specimens;
	}

	/**
	 * Getter of the property <tt>editionDate</tt>
	 * 
	 * @return Returns the editionDate.
	 * @uml.property name="editionDate"
	 */
	public Calendar getEditionDate() {
		return editionDate;
	}

	/**
	 * Setter of the property <tt>editionDate</tt>
	 * 
	 * @param editionDate
	 *            The editionDate to set.
	 * @uml.property name="editionDate"
	 */
	public void setEditionDate(Calendar editionDate) {
		this.editionDate = editionDate;
	}

	public int getNbCreatedSpecimen() {
		return nbCreatedSpecimen;
	}

	public void setNbCreatedSpecimen(int nbCreatedSpecimen) {
		this.nbCreatedSpecimen = nbCreatedSpecimen;
	}

	public Set<Specimen> getSpecimens() {
		return specimens;
	}

	public void setNbAvailableSpecimen(int nbAvailableSpecimen) {
		this.nbAvailableSpecimen = nbAvailableSpecimen;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public Media(){};

	public Media(String title, Calendar editionDate,
			String type, String isbnOrIsan, int nbSpecimen) throws BadParametersException, MediaExistsException{

		if ((title == null)  || (editionDate == null)
				|| (type == null))
			throw new BadParametersException();

		this.title = title;
		this.editionDate = editionDate;
		this.type=type;
		this.isbnOrIsan = isbnOrIsan;
		this.specimens= new HashSet<Specimen>();
		for (int i=0;i<nbSpecimen;i++){
			Specimen spec= new Specimen(this);
			this.addSpecimen(spec);
		}
		this.loans=new HashSet<Loan>();

	}

	/**
	 * increment the value of created specimens of a media
	 */
	public void incrementCreatedSpecimens(){
		this.nbCreatedSpecimen++;
	}

	/**
	 * decrement the value of created specimens of a media
	 */
	public void decrementCreatedSpecimens(){
		this.nbCreatedSpecimen--;
	}
	
	public Specimen availableExemplary() {
		Specimen specimen= null;
		for(Specimen spec : specimens){
			if (!(spec.isLent())){
				specimen=spec;
				break;
			}
		}
		return specimen;
	}

	/**
	 * @throws BadParametersException 
	 * @throws MediaExistsException 
	 */
	public void addSpecimen(Specimen specimen) throws BadParametersException, MediaExistsException{
		if (specimen == null)
			throw new BadParametersException();
		if (IsASpecimen(specimen))
			throw new MediaExistsException();

		this.specimens.add(specimen);
		this.incrementCreatedSpecimens();
		nbAvailableSpecimen++;
	}
	
	/**
	 * @throws BadParametersException 
	 * @throws MediaExistsException 
	 */
	public void removeSpecimen(Specimen specimen) throws BadParametersException, MediaExistsException{
		if (specimen == null)
			throw new BadParametersException();
		if (IsASpecimen(specimen))
			throw new MediaExistsException();

		this.specimens.remove(specimen);
		this.decrementCreatedSpecimens();
		nbAvailableSpecimen--;
	}
	
	/**
	 * 
	 * @throws BadParametersException
	 * @throws MediaExistsException
	 */
	public void addSpecimen() throws BadParametersException, MediaExistsException{
		Specimen specimen = new Specimen(this);
		this.specimens.add(specimen);
		this.incrementCreatedSpecimens();
		nbAvailableSpecimen++;
	}

	private boolean IsASpecimen(Specimen specimen) throws BadParametersException {

		if (specimen == null)
			throw new BadParametersException();

		// If the object is in the list,
		// it is a known exemplary
		return specimens.contains(specimen);
	}


	public Specimen lend(Loan loan) throws  LentMediaException, BadParametersException  {
		if (loan == null)
			throw new BadParametersException();
		
		Specimen exemplary= availableExemplary();
		if (exemplary==null)
			throw new LentMediaException("Not available specimens for this media.");
		exemplary.lend(loan);
		this.loans.add(loan);
		nbAvailableSpecimen--;
		return  exemplary;
	}
	
	/**
	 * @return the lentSpecimens
	 */
	public Integer getLentSpecimens() {
		return lentSpecimens;
	}

	/**
	 * @param lentSpecimens the lentSpecimens to set
	 */
	public void setLentSpecimens(Integer lentSpecimens) {
		this.lentSpecimens = lentSpecimens;
	}

	/**
	 * Returns a description of the media
	 */
	public String toString() {
		return this.title;
	}


}
